package com.cat2bug.junit.service.report;

import com.alibaba.fastjson.JSON;
import com.cat2bug.junit.annotation.PushReport;
import com.cat2bug.junit.service.CompileClassResultService;
import com.cat2bug.junit.service.IReport;
import com.cat2bug.junit.service.FunctionTestClassReportService;
import com.cat2bug.junit.type.DefectState;
import com.cat2bug.junit.type.DefectType;
import com.cat2bug.junit.type.PushReportType;
import com.cat2bug.junit.util.AppUtils;
import com.cat2bug.junit.vo.DefectVo;
import com.cat2bug.junit.vo.ReportVo;
import kotlin.Result;
import okhttp3.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.logging.log4j.util.Strings;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 推送报告服务
 */
public class PushReportService implements IReport<String> {
    private static final Log logger = LogFactory.getLog(PushReportService.class);
    private static final String CONFIG_FILE = "cat2bug.properties"; // YAML 配置文件路径
    /**
     * 默认API服务地址
     */
    private final static String DEFAULT_API_HOST = "https://www.cat2bug.com/cloud/";
    /**
     * 接口提交TOKEN标识
     */
    private static final String AUTH_TOKEN_HEADER_NAME = "CAT2BUG-API-KEY";
    /**
     * 推送缺陷api地址
     */
    private static final String PUSH_DEFECT_URL = "/api/report/defect";
    /**
     * 接口内容类型
     */
    private static final MediaType FORM_CONTENT_TYPE = MediaType.parse("application/json;charset=utf-8");

    private List<AbstractTableReport> reportList = new ArrayList<>();

    private Class<?> testCaseClass;

    public PushReportService(Class<?> testCaseClass, AbstractTableReport... list){
        this.testCaseClass = testCaseClass;
        this.reportList.addAll(Arrays.stream(list).collect(Collectors.toList()));
    }

    @Override
    public String getTitle() {
        return "提交测试报告";
    }

    @Override
    public String getReport() {
        if(this.testCaseClass==null) return null;
        PushReport pushReport = this.testCaseClass.getAnnotation(PushReport.class);
        if(pushReport==null || pushReport.isPush()==false) return null;

        String url = this.getPushDefectUrl(pushReport);
        if(Strings.isBlank(url)) return null;

        String projectKey = this.getProjectKey(pushReport);
        if(Strings.isBlank(projectKey)) return null;

        String version = this.getVersion(pushReport);

        String handler = this.getHandler(pushReport);

        StringBuffer sb = new StringBuffer();
        sb.append(String.format("推送地址:%s\n处理人:%s\n",url,handler));


        List<DefectVo> defectList = new ArrayList<>();
        PushReportType pushReportType = this.getPushType(pushReport);
        // 添加测试成功的用例
        if(pushReportType==PushReportType.All) {
            // 统计所有成功的组key
            Set<String> successGroupKeySet = new HashSet<>();
            FunctionTestClassReportService.getSuccessDescription().values().stream().forEach(vs->{
                for(Description d : vs) {
                    successGroupKeySet.add(getGroupKey(d));
                }
            });

            // 统计所有失败的组key
            Set<String> failGroupKeySet = new HashSet<>();
            FunctionTestClassReportService.getFailDescription().values().stream().forEach(vs->{
                for(Failure f : vs) {
                    failGroupKeySet.add(getGroupKey(f.getDescription()));
                }
            });

            successGroupKeySet.removeAll(failGroupKeySet); // 移除有失败的测试组key
            successGroupKeySet.forEach(s->{
                DefectVo defect = new DefectVo();
                defect.setDefectGroupKey(s);
                defect.setDefectState(DefectState.CLOSED);
                defectList.add(defect);
            });
        }
        // 添加测试失败的用例
        FunctionTestClassReportService.getFailDescription().entrySet().stream().forEach(f->{
            List<DefectVo> tempList = f.getValue().stream().map(d->failure2Defect(d,version)).collect(Collectors.toList());
            defectList.addAll(tempList);
        });
        // 创建测试报告对象
        ReportVo<List<DefectVo>> reportVo = new ReportVo();
        reportVo.setHandler(handler);
        reportVo.setReportData(defectList);
        reportVo.setReportTime(new Date());
        reportVo.setReportTitle("单元测试报告:"+this.testCaseClass.getName());
        String reportDescription = Arrays.asList(
                this.getEnvironmentInfo(),
                this.getDataBaseInfo(),
                this.getDataDetails()
        ).stream().collect(Collectors.joining("\n"));
        reportVo.setReportDescription(reportDescription);

        // 推送报告到Bug系统
        try {
            this.pushReport(
                    url,
                    projectKey,
                    reportVo);
            sb.append("推送报告成功\n");
        } catch (Exception e) {
            logger.error(e);
        }
        return sb.toString();
    }

    private DefectVo failure2Defect(Failure f,String version) {
        DefectVo defect = new DefectVo();
        defect.setModuleVersion(version);
        String defectName = "["+f.getDescription().getDisplayName()+"]"+f.getException().toString();
        defect.setDefectName(defectName);
        StringBuffer describe = new StringBuffer();
        describe.append(this.getEnvironmentInfo());
        describe.append("\n``` java");
        describe.append(f.getTrace());
        describe.append("\n```");
        defect.setDefectDescribe(describe.toString());
        defect.setDefectState(DefectState.PROCESSING);
        defect.setDefectType(DefectType.BUG);
        String groupKey = f.getDescription().getClassName()+"."+f.getDescription().getMethodName();
        defect.setDefectGroupKey(groupKey);
        defect.setDefectKey(groupKey+":"+f.getException().toString());
        return defect;
    }

    /**
     * 获取组标识
     * @param d
     * @return
     */
    private String getGroupKey(Description d) {
        return d.getClassName()+"."+d.getMethodName();
    }

    /**
     * 获取环境信息
     * @return
     */
    private String getEnvironmentInfo() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String osName = System.getProperty("os.name");// 操作系统名称
        String osArch = System.getProperty("os.arch");// 获取操作系统架构（位数）
        String osVersion = System.getProperty("os.version");// 获取操作系统版本
        String javaVersion = System.getProperty("java.version"); // java版本
        List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments(); // jvm参数
        return Arrays.asList(
                "## 基础信息",
                "* **Report Time:** "+sdf.format(new Date()),
                "* **Tools:** Cat2Bug-Spring-Boot-JUnit",
                "* **Tools Version:** "+AppUtils.getAppVersion(),
                "* **OS Name:** "+osName,
                "* **OS Version:** "+osVersion,
                "* **OS Arch:** "+osArch,
                "* **Java Version:** "+javaVersion,
                "* **JVM Args:** "+jvmArgs.stream().collect(Collectors.joining(";"))
        ).stream().collect(Collectors.joining("\n"));
    }

    private String getDataBaseInfo() {
        String ret = this.reportList.stream().map(r->{
            StringBuffer sb = new StringBuffer();
            sb.append("### "+r.getTitle()+"\n");
//            sb.append(":::success\n");
//            sb.append("测试数据成功了，哈哈哈哈哈\n");
//            sb.append(":::\n");
//            sb.append("```echarts\n");
//            sb.append("{\n" +
//                    "  \"width\": 800,\n" +
//                    "  \"height\": 300,\n" +
//                    "  \"legend\": {},\n" +
//                    "  \"tooltip\": {},\n" +
//                    "  \"dataset\": {\n" +
//                    "    \"source\": [\n" +
//                    "      [\"product\", \"2012\", \"2013\", \"2014\", \"2015\"],\n" +
//                    "      [\"Matcha Latte\", 41.1, 30.4, 65.1, 53.3],\n" +
//                    "      [\"Milk Tea\", 86.5, 92.1, 85.7, 83.1],\n" +
//                    "      [\"Cheese Cocoa\", 24.1, 67.2, 79.5, 86.4]\n" +
//                    "    ]\n" +
//                    "  },\n" +
//                    "  \"xAxis\": [\n" +
//                    "    { \"type\": \"category\", \"gridIndex\": 0 },\n" +
//                    "    { \"type\": \"category\", \"gridIndex\": 1 }\n" +
//                    "  ],\n" +
//                    "  \"yAxis\": [{ \"gridIndex\": 0 }, { \"gridIndex\": 1 }],\n" +
//                    "  \"grid\": [{ \"bottom\": \"55%\" }, { \"top\": \"55%\" }],\n" +
//                    "  \"series\": [\n" +
//                    "    { \"type\": \"bar\", \"seriesLayoutBy\": \"row\" },\n" +
//                    "    { \"type\": \"bar\", \"seriesLayoutBy\": \"row\" },\n" +
//                    "    { \"type\": \"bar\", \"seriesLayoutBy\": \"row\" },\n" +
//                    "    { \"type\": \"bar\", \"xAxisIndex\": 1, \"yAxisIndex\": 1 },\n" +
//                    "    { \"type\": \"bar\", \"xAxisIndex\": 1, \"yAxisIndex\": 1 },\n" +
//                    "    { \"type\": \"bar\", \"xAxisIndex\": 1, \"yAxisIndex\": 1 },\n" +
//                    "    { \"type\": \"bar\", \"xAxisIndex\": 1, \"yAxisIndex\": 1 }\n" +
//                    "  ]\n" +
//                    "}\n");
//
//            sb.append("```\n");
//            sb.append("\n\n");

            sb.append(" | "+Arrays.stream(r.getTableHeader()).collect(Collectors.joining(" | "))+" | \n");
            sb.append(" | "+Arrays.stream(r.getTableHeader()).map(m->"---------").collect(Collectors.joining(" | "))+" | \n");
            for(String[] data : r.getTableData()){
                sb.append(" | "+Arrays.stream(data).collect(Collectors.joining(" | "))+" | \n");
            }
            return sb.toString();
        }).collect(Collectors.joining("\n"));
        return ret;
    }

    private String getDataDetails() {
        StringBuffer ret = new StringBuffer();
        ret.append("## 数据报告详情\n");
        ret.append(CompileClassResultService.getFailResults().entrySet().stream().map(f->{
            StringBuffer sb = new StringBuffer();
            sb.append("* Class Name:"+f.getKey()+"\n");
            sb.append(f.getValue().getMessage()+"\n");
            return sb.toString();
        }).collect(Collectors.joining("\n")));

        ret.append(FunctionTestClassReportService.getFailDescription().entrySet().stream().map(f->{
            StringBuffer sb = new StringBuffer();
            sb.append("### "+f.getKey().getName()+"\n\n");
            sb.append(f.getValue().stream().map(failure->{
                StringBuffer fsb = new StringBuffer();
                fsb.append("* Method Name: "+failure.getDescription().getMethodName()+"\n\n");
                fsb.append("```java\n");
                fsb.append(failure.getTrace()+"\n");
                fsb.append("```\n");
                return fsb.toString();
            }).collect(Collectors.joining("\n")));
            return sb.toString();
        }).collect(Collectors.joining("\n")));
        return ret.toString();
    }

    private void pushReport(String url, String projectKey, ReportVo report) throws IOException {
        OkHttpClient client = new OkHttpClient();
        String body = JSON.toJSONString(report);
        RequestBody formBody = RequestBody.create(FORM_CONTENT_TYPE, body);
        Request request = new Request.Builder()
                .url(url)
                .header("content-type", "application/json")
                .header(AUTH_TOKEN_HEADER_NAME,projectKey)
                .post(formBody).build();

        Response response = client.newCall(request).execute();

        if (response.code() != 200) {
            new RuntimeException("提交问题接口失败\nstate code:" + response.code() + "\n body:" + response.body().string());
        }
    }

    /**
     * 获取推送类型
     * @param pushReport    推送注解参数
     * @return 推送类型
     */
    private PushReportType getPushType(PushReport pushReport) {
        if(pushReport.type()!=PushReportType.DEFAULT)
            return pushReport.type();
        else
            return AppUtils.getPushType();
    }

    /**
     * 获取版本号
     * @param pushReport  推送注解参数
     * @return 版本号
     */
    private String getVersion(PushReport pushReport) {
        if(Strings.isNotBlank(pushReport.version())){
            return pushReport.version();
        }
        String version = AppUtils.getPushVersion();
        if(Strings.isNotBlank(version)) {
            return version;
        }
        return null;
    }

    /**
     * 获取处理人
     * @param pushReport 推送注解参数
     * @return	推送人
     */
    private String getHandler(PushReport pushReport) {
        if(Strings.isNotBlank(pushReport.handler())){
            return pushReport.handler();
        }
        String handler = AppUtils.getPushHandler();
        if(Strings.isNotBlank(handler)) {
            return handler;
        }
        return null;
    }

    /**
     * 获取项目密钥
     * @param pushReport	推送注解参数
     * @return	密钥
     */
    private String getProjectKey(PushReport pushReport) {
        if(Strings.isNotBlank(pushReport.projectKey())){
            return pushReport.projectKey();
        }
        String projectKey = AppUtils.getPushProjectKey();
        if(Strings.isNotBlank(projectKey)) {
            return projectKey;
        }
        return null;

    }

    /**
     * 获取缺陷推送地址
     * @param pushReport	推送注解对数
     * @return	缺陷服务器地址
     */
    private String getPushDefectUrl(PushReport pushReport) {
        if(Strings.isNotBlank(pushReport.host())){
            return pushReport.host().replaceAll("/$","")+PUSH_DEFECT_URL;
        }
        String host = AppUtils.getPushUrl();
        if(Strings.isNotBlank(host)) {
            return host.replaceAll("/$","")+PUSH_DEFECT_URL;
        }
        return null;
    }
}
